/******************************************************************************
 * \brief	私有通信协议
 * \note	File format: UTF-8，中文编码：UTF-8
 * \author	将狼才鲸
 * \date	2023-03-25
 ******************************************************************************/

#ifndef _SCP_PROTOCOL_H_
#define _SCP_PROTOCOL_H_

/*********************************** 头文件 ***********************************/
#include "util_errno.h"
#include "util_config.h"
#include "util_comm.h"
#include "util_print.h"
#include "queue_buffer.h"
#include "queue_buffer_cfg.h"

/*
 * 简介：
 *		通信协议是分层的，不同层对数据包有不同说法：
 *		链路层叫帧frame，网络层叫包packet，传输层叫段segment，应用层叫消息message
 *		消息里面加header变成段，段里面加header变成包，包里面加header变成帧，
 *		帧通过物理或虚拟线路发送出去本通信模块位于传输层和应用层
 * 功能：
 *		支持一次发送一系列包，发送方和接收方都有缓存
 *		通信链路上的实际发送的数据内容：
 *		4字节魔术字起始码 + seg_pkt_head_t + 实际消息数据
 * 包含关系：
 *		段句柄中有设备句柄，设备句柄中包含网络设备等句柄
 * 通信过程中要申请的空间：
 *		通信链路建立时申请一个scp_t和一个scpdev_t，
 *		以后每个消息发送或者响应时申请一个segment_t
 */

/*********************************** 宏定义 ***********************************/
/* 一个消息系列允许最多包个数 */
#define SCP_MAX_VCID_VALID    (8)
#define SCP_MAX_VSID_VALID    (8)

/* 长度调整到字节对齐，Alignment must be power of 2. */
#define ALIGN_TO(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment) - 1))
#define SCP_MAGIC	0xAABBCCDD	/* 魔术字，便于流式设备一个字节一个字节的读数据时判断设备帧的包头 */
#define SCP_TYPE	0xFF		/* 传输层协议类型，当前就一个协议，私有通信协议SCP */

#define SEG_PKT_HEAD_LEN	(sizeof(seg_pkt_head_t))	/* 包头长度 */

/**
 * \brief	生成协议id
 */
#define SCP_RPID_MARK		(0x8000)	/* 响应包协议ID最高位为1，responds packet mask */
#define SCP_QPID_MASK		(0x7fff)	/* 请求包协议ID最高位为0 */
#define SCP_RPID(pid)		(SCP_RPID_MARK | (pid))	/* 将协议ID转成响应包 */
#define SCP_QPID(pid)		(SCP_QPID_MASK & (pid))	/* 将协议ID转成请求包 */
#define SCP_PID(g, n)		((((g) & 0x7f) << 8) | ((n) & 0xff))	/* 用组别和递增ID生成包协议ID */
#define SCP_PIDG(pid)		(((pid) >> 8) & 0x7f)	/* 从协议ID中获取组别信息 */
#define SCP_PIDP(pid)		((pid) & 0xff)			/* 从协议ID中获取协议信息 */

/**
 * \brief	用户组和操作权限控制，很少用到此功能
 */
#define SEC_UG            0x40	/* 紧急urgency，支持system及以下读写 */
#define SEC_SW            0x20	/* system，系统级别操作 */
#define SEC_SR            0x10
#define SEC_SRW           (SEC_SR | SEC_SW)
#define SEC_TW            0x08	/* terminal，终端级别操作 */
#define SEC_TR            0x04
#define SEC_TRW           (SEC_TR | SEC_TW)
#define SEC_PW            0x02	/* protocol，单次连接级别操作 */
#define SEC_PR            0x01
#define SEC_PRW           (SEC_PR | SEC_PW)
#define SEC_NO            0x00

/********************************** 类型定义 **********************************/
/**
 * \brief	用户组级别，用于权限控制，预留，当前未使用该功能
 */
enum {
	SECLVL_URGENCY = 0x00, /* level: urgency operator，紧急操作，支持system系统及以下操作权限 */
	SECLVL_ADMIN,          /* administrator，管理员，支持terminal终端及以下操作权限 */
	SECLVL_OPERATOR,       /* operator，操作员权限，支持protocol协议控制权限 */
	SECLVL_USER,           /* normal user，普通用户，支持protocol协议内容读内容 */
	SECLVL_COUNT
};

/**
 * \brief	客户端发出的数据包是否需要服务器端返回数据，transfer type
 */
enum {
	TT_NONE = 0,	/* 传输类型，transfer type */
	TT_ACKONLY,		/* 接收方只需回复确认包 */
	TT_RESPONDS,	/* 接收方需响应具体的数据 */
};

/**
 * \brief	包发送状态
 */
enum {
	SEGMENT_ISNULL      = 0x00,	/* the segment is blank */
	SEGMENT_TRANSMIT1   = 0x01, /* 数据包已发送出去，the segment need transmit  */
	SEGMENT_TRANSMIT2   = 0x02,	/* 允许重发2次，总共发送3次，当前未使用重传机制 */
	SEGMENT_TRANSMIT3   = 0x04,
	SEGMENT_TRANSFERING = 0x08, /* 数据包正在发送中，the segment has transmitted, need respond  */
};

/**
 * \brief	包反馈类型，acknowledge signal
 */
typedef enum {
	ACK_NULL = 0x00, /* 无确认信息 */
	ACK_ACK0 = 0x01, /* 正确接收，当前未使用 */
	ACK_ACK1 = 0x02, /* 对第1次发送的确认信息 */
	ACK_ACK2 = 0x03, /* 对第2次发送的确认信息，当前未使用重传机制 */
	ACK_ACK3 = 0x04, /* 对第3次发送的确认信息，当前未使用重传机制 */
	ACK_NACK = 0x05, /* 传输超时或者服务器不支持该发送包，worker transmit timeout */
	ACK_FACK = 0x06, /* 传输错误，telecommuting transmit fault */
	ACK_RACK = 0x07, /* 需要重传，retransmit requirement，当前未使用重传机制  */
	ACK_EXTA = 0x08, /* 额外的确认信息，extra acknowledge */
} SCP_ACK;

/**
 * \brief	应用层通信包类型的组别，protocol group
 * \details	除了ACK，都不是最终的应用层协议ID，这里的分类主要用来划分模块，
 *			比如SCP_PG_DEBUG组别的协议处理都放在scp_client_debug.c这个文件中
 */
enum {
	SCP_PID_NONE = 0,
	SCP_PID_ACK = 0x01,		/* 响应包；内容就响应包头+原始接收包的包头+响应数据（如果有） */

	SCP_PG_MSG = 0x02,		/* SCP协议的通用读写 */
	SCP_PG_DEBUG = 0x03,	/* 调试信息 */
	SCP_PG_SYSTEM = 0x04,	/* 控制对方设备的寄存器读写，从底层控制对方设备	*/
	SCP_PG_DISP,			/* GUI底层控制 */
};

/**
 * \brief	应用层协议类型
 * \details	具体的包类型，也就是具体的通信协议，protocol group，在PID中赋值；
 *			在一次通信连接中可以使用多个应用层协议
 */
enum {
	/* 正常主从设备间的数据收发 */
	SCP_PG_MSG_READ = SCP_PID(SCP_PG_MSG, 0x82),      // READ
	SCP_PG_MSG_WRITE = SCP_PID(SCP_PG_MSG, 0x83),     // WRITE

	/* 调试用 */
	SCP_PG_DEBUG_PUTS = SCP_PID(SCP_PG_DEBUG, 0x01),    /* 打印字符串 */
	SCP_PG_DEBUG_GETCHAR = SCP_PID(SCP_PG_DEBUG, 0x02),	/* 获取服务器端终端输入 */
	SCP_PG_DEBUG_LOOPBACK = SCP_PID(SCP_PG_DEBUG, 0x03),	/* 主机和从机通信回环测试 */

	/* 对设备的底层操作，寄存器读写 */
	SCP_PG_SYSTEM_IOR = SCP_PID(SCP_PG_SYSTEM, 0x01),   /* 寄存器读 */
	SCP_PG_SYSTEM_IOW = SCP_PID(SCP_PG_SYSTEM, 0x02),   /* 寄存器写 */
	SCP_PG_SYSTEM_IOM = SCP_PID(SCP_PG_SYSTEM, 0x03),   /* 内存拷贝 */
	SCP_PG_SYSTEM_EXIT = SCP_PID(SCP_PG_SYSTEM, 0xFF),  /* 退出SCP通信 */
};

typedef struct _scp_dev scp_dev_t;	/* 不使用头文件包含，而能直接声明scp_devs.h里面的类型定义 */

/* 段数据头 */
typedef struct _seg_pkt_head {
	/* 链路层8字节 */
	uint32_t  CID;   /* 连接ID，connection id，发送的不同包CID不同，一个发送包和对应的响应包相同 */
	uint32_t  LEN;   /* 这是发送的有效数据长度 */

	/* 传输层16字节 */
	uint32_t  SID;   /* 段ID，segment id，发送的不同包SID不同，发送包和对应的响应包SID相同 */
	uint8_t   TYPE;  /* 传输层协议类型，当前只支持SCP_TYPE */
	uint8_t   SEC;   /* 段操作权限，segment security */
	uint16_t  PID;   /* 应用层协议ID，protocol id，如SCP_PG_DEBUG_LOOPBACK...在响应包中会加入响应MASK */
	uint32_t  SUB;   /* 响应包类型如ACK_ACK1等，protocol sub id  */
	uint32_t  CRC;   /* CRC校验 */
} PACKED seg_pkt_head_t;

/**
 * \brief	数据段句柄
 * \details	取名segment表示其位于传输层，一个segment段中携带一条通信消息
 *			该结构体占用152~204字节，需要小于QBUF_GROUP_TMPBUF缓存的512字节
 */
typedef struct _segment {
	struct list_head node;	/* 数据段句柄中携带本次消息的节点，用于在一系列消息中加入到发送队列中取 */
	int TT;  /* 数据包类型（是否需要对方反馈数据），segment transfer type */
	int PHS;  /* 发送包的状态，segment phase */

	int requests; /* requests, noused ? */
	int responds; /* 已收到的响应包类型，如ACK_ACK1... */

	uint32_t LEN; /* segment data length */
	unsigned long XTM; /* 传输时的时间，transmit time */
	
	qbuf_t *qbuf;	/* segment_t自己所在的缓存 */
	qbuf_t *req_qbuf, *rsp_qbuf;	/* 请求和响应缓存 */

	/* next用于实现一次性发送一系列消息（较少会用到），
	   respseg存放对方回复的消息； */
	struct _segment *next, *respseg;
	scp_dev_t *pdev;

	/* 前面的60字节是传输数据帧私有数据，暂未使用，后面4字节存入的只有魔术字SCP_MAGIC */
#define SEGMENT_USER_SIZE	64
	/* &private_data[SEGMENT_USER_SIZE - 4]是最终通过链路发出去的数据；
	   发送的数据是4字节魔术字起始码 + seg_pkt_head_t成员 + 有效数据；
	   当前允许的总长度只有36字节，可以自行将其改大 */
	uint8_t private_data[SEGMENT_USER_SIZE + 32];
} segment_t;

/********************************** 接口函数 **********************************/
/**
 * \brief	从通信片段segment_t中获取名为private_data的成员
 */
extern uint8_t *scpseg_pri(segment_t *seg);

/**
 * \brief	从通信片段segment_t的private_data成员中获取seg_pkt_head_t实际包数据
 */
extern uint8_t *scpseg_data(segment_t *seg);

// extern segment_t *SEG_FROM_PRI(void *pri);
// extern segment_t *SEG_FROM_DATA(void *dat);

/**
 * \brief	申请数据段句柄空间
 */
extern segment_t *scpseg_new(scp_dev_t *pdev);

// extern bool Scp_SegmentExpandRomdata(segment_t *seg, bool beReq, bool bRsp);

/**
 * \brief	要发送的通信包是否存在
 */
extern bool scpseg_valid(segment_t *seg);

/**
 * \brief	删除一个通信包
 */
extern bool scpseg_free(segment_t *seg);

#endif /* _SCP_PROTOCOL_H */

/*********************************** 文件尾 ***********************************/
